/*
 * $Id: ProtocolCompiler.java 1259 2007-01-09 14:23:47Z tcoupaye $
 *
 * Behavior Protocols extensions for static and runtime checking
 * developed for the Julia implementation of Fractal.
 *
 * Copyright (C) 2006
 *    Formal Methods In Software Engineering Group
 *    Institute of Computer Science
 *    Academy of Sciences of the Czech Republic
 *
 * Copyright (C) 2006 France Telecom
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2 of the License, or (at your option) any later version.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
 *
 * Contact: ft@nenya.ms.mff.cuni.cz
 * Authors: Tomas Bures <bures@nenya.ms.mff.cuni.cz>
 *
 */

package org.objectweb.fractal.bpc.adl;

import java.io.IOException;
import java.io.InputStreamReader;
import java.io.Reader;
import java.net.URL;
import java.util.List;
import java.util.Map;

import org.objectweb.deployment.scheduling.component.api.InstanceProviderTask;
import org.objectweb.deployment.scheduling.component.lib.AbstractConfigurationTask;
import org.objectweb.fractal.adl.ADLException;
import org.objectweb.fractal.adl.Node;
import org.objectweb.fractal.adl.TaskMap;
import org.objectweb.fractal.adl.components.ComponentContainer;
import org.objectweb.fractal.adl.components.PrimitiveCompiler;
import org.objectweb.fractal.api.control.BindingController;

/**
 * A {@link PrimitiveCompiler} to compile {@link Protocol} nodes in definitions.  
 */
public class ProtocolCompiler implements PrimitiveCompiler, BindingController {
 
  /**
   * Name of the mandatory interface bound to the {@link ProtocolBuilder} used 
   * by this compiler.
   */
  public final static String BUILDER_BINDING = "builder";

  /**
   * The {@link ProtocolBuilder} used by this compiler.
   */
  private ProtocolBuilder builder; // the builder client interface
  
  /**
   * Counter used to assign explicit ordering to protocols
   */
  private int order;

  // --------------------------------------------------------------------------
  // Implementation of the BindingController interface
  // --------------------------------------------------------------------------
  
  public String[] listFc() {
    return new String[] { BUILDER_BINDING };
  }

  public Object lookupFc (final String itf) {
    if (itf.equals(BUILDER_BINDING)) {
      return builder;
    }
    return null;
  }

  public void bindFc (final String itf, final Object value) {
    if (itf.equals(BUILDER_BINDING)) {
      builder = (ProtocolBuilder)value;
    }
  }

  public void unbindFc (final String itf) {
    if (itf.equals(BUILDER_BINDING)) {
      builder = null;
    }
  }
  
 
  // --------------------------------------------------------------------------
  // Implementation of the Compiler interface
  // --------------------------------------------------------------------------
   
  public void compile (List path, ComponentContainer container, TaskMap tasks, Map context)
    throws ADLException
  {
    if (container instanceof ProtocolContainer) {
      Protocol prot = ((ProtocolContainer)container).getProtocol();
      if (prot != null) {
        InstanceProviderTask createTask = (InstanceProviderTask)tasks.getTask("create", container);
        
        String protocol;
        
        if (prot.getValue()==null && prot.getFile()==null) {
            throw new ADLException("Either 'value' or 'file' attribute has to be set.",(Node)prot);
            
        } else if (prot.getValue()!=null && prot.getFile()!=null) {
            throw new ADLException("Only one of 'value' and 'file' attribute may be set.",(Node)prot);
            
        } else if (prot.getValue()!=null) {
            protocol=prot.getValue();
            System.out.println("Assigning order "+order+" to protocol '"+protocol+"'.");
        } else {
            try {
                URL protoFileURL = ProtocolCompiler.class.getResource("/"+prot.getFile());
                if (protoFileURL==null) {
                    throw new ADLException("Resource not found: '/"+prot.getFile()+"'",(Node)container);
                }
                
                Reader ir=new InputStreamReader(protoFileURL.openStream());
                
                StringBuffer outsb=new StringBuffer();
                char[] buffer=new char[1024];
                int charsRead;
                
                while ((charsRead=ir.read(buffer))!=-1) {
                    outsb.append(buffer,0,charsRead);
                }
                
                protocol=outsb.toString();

                System.out.println("Assigning order "+order+" to file with a protocol '"+prot.getFile()+"'.");
            } catch (IOException e) {
                throw new ADLException("Cannot read protocol from external file.",(Node)prot,e);
            }
        }
        
        SetProtocolTask task = new SetProtocolTask(builder, protocol, order);
        task.setInstanceProviderTask(createTask);
        tasks.addTask("protocol", container, task);
        
        order++;
      }
    }
  }
 
  /**
   * A task assigning a protocol to a component.
   */
  static class SetProtocolTask extends AbstractConfigurationTask {

    private ProtocolBuilder builder;
   
    private String protocol;
    
    private int order;
   
    public SetProtocolTask (ProtocolBuilder builder, String protocol, int order) {
      this.builder=builder;
      this.protocol=protocol;
      this.order=order;
    }
   
    public void execute (final Object context) throws Exception {
      Object component = getInstanceProviderTask().getResult();
      builder.setProtocol(component, protocol, order);
    }

    public Object getResult() { 
      return null; 
    }
    
    public void setResult (Object result) {
    }
  }
}
